<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>元素拖动</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

    #draggable {
      width: 100px;
      height: 100px;
      background-color: #3498db;
      position: absolute;
      cursor: grab;
      left: 0;
      top: 0;
      transform: translate(var(--x, 0), var(--y, 0));
    }
  </style>
</head>

<body>
  <div id="draggable"></div>

  <script>
    let offsetX, offsetY
    let isDragging = false
    const draggableElement = document.getElementById('draggable')

    function start(x, y) {
      isDragging = true

      // 记录鼠标点击位置与元素左上角的偏移
      const { left, top } = draggableElement.getBoundingClientRect()
      offsetX = x - left
      offsetY = y - top

      // 修改鼠标样式
      draggableElement.style.cursor = 'grabbing'
    }

    function move(x, y) {
      if (!isDragging) return

      // 计算新的元素位置
      const dx = x - offsetX
      const dy = y - offsetY

      // 限制元素不超出视口范围
      const maxX = window.innerWidth - draggableElement.offsetWidth
      const maxY = window.innerHeight - draggableElement.offsetHeight

      const constrainedX = Math.max(0, Math.min(dx, maxX))
      const constrainedY = Math.max(0, Math.min(dy, maxY))

      // 移动元素
      draggableElement.style.setProperty('--x', `${constrainedX}px`)
      draggableElement.style.setProperty('--y', `${constrainedY}px`)
    }

    function stop() {
      isDragging = false
      draggableElement.style.cursor = 'grab'
    }

    // 鼠标
    draggableElement.addEventListener('mousedown', (e) => {
      start(e.clientX, e.clientY)
    })
    document.addEventListener('mousemove', (e) => {
      move(e.clientX, e.clientY)
    })
    document.addEventListener('mouseup', stop)

    // 触摸
    draggableElement.addEventListener('touchstart', (e) => {
      const touch = e.touches[0]
      start(touch.clientX, touch.clientY)
    })
    document.addEventListener('touchmove', (e) => {
      const touch = e.touches[0]
      move(touch.clientX, touch.clientY)
    })
    document.addEventListener('touchend', stop)
    document.addEventListener('touchcancel', stop)
  </script>
</body>

</html>